Skip to content

Conversation

VegetarianOrc
Copy link

What was changed

This PR adds code generation of the RPC call wrapper in the bridge client to ensure ease the maintenance of manually updating the methods when core has a proto update. This generation has been added to the end of the gen_protos_docker script.

Some changes were included to make the generation possible:

  1. To eliminate a code generation path, the rpc_call and rpc_call_on_trait macros have been refactored to only include an exported macro named rpc_call that uses the fully qualified call syntax that was previously in the body of rpc_call_on_trait.
  2. The multiple-pymethods feature of PyO3 was enabled to allow the ClientRef impl to be split into two modules.
  3. The visibility of some elements of client module was elevated to pub(crate) for referencing from the client_rpc_generated module.

Why?

This allows us to easily keep the bridge client up to date with the RPC methods exposed via core.

Checklist

  1. Closes [Bug] Batch operation feature in the Temporal Python SDK doesn't work #927

  2. How was this tested:

The generated client was tested against the existing test suite and used to ensure the repro described in #927 correctly functions against a Temporal dev server.

  1. Any docs updates needed?

@VegetarianOrc VegetarianOrc requested a review from a team as a code owner September 25, 2025 21:27
@CLAassistant
Copy link

CLAassistant commented Sep 25, 2025

CLA assistant check
All committers have signed the CLA.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we might also want to generate all of that mess in temporalio/service.py. Off the top of my head, what I would suggest is to generate some file at temporalio/bridge/client_generated.py.

Could do the simple way of just having an init_workflow_service_client_calls(client: ServiceClient) that just does what it does today which is makes a _new_call for each known service name. Alternatively, a cleaner approach may be to just move the WorkflowService (and operator and cloud equivalents) to this generated file and have explicit methods created for each call that do as expected and then alias the service class publicly (or have the public WorkflowService class extend the internal/bridge form).

Copy link
Member

@cretz cretz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM overall, just little things

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO we should cargo fmt this and poe format the Python gen'd code upon generation

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding the cargo fmt will format the entire bridge source so will generate additional changes for the PR but it seems worth it to me.

import temporalio.api.workflowservice.v1
import temporalio.bridge.client
import temporalio.bridge.proto.health.v1
import temporalio.bridge.services_generated as svc_gen
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
import temporalio.bridge.services_generated as svc_gen
import temporalio.bridge.services_generated

Pedantic and not technically required, but usually we fully qualify each use when referring to ourselves in these files

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated these to be fully qualified. I also moved the generated python to be in bridge/generated so I could add a pydocstyle exclusion for the file. If that doesn't seem reasonable, I can see about spitting out some autogen'd doc strings.

Copy link
Member

@cretz cretz Sep 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do think that' be nice. Yeah, one issue about moving the base class into the bridge area is that it won't be as clear on https://python.temporal.io (we consider the bridge private code and don't show it in API docs). This will be the first time user-facing code references the bridge module (in this case for a class extension). But I don't think it's that big of a deal.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not huge, but I don't see any good reason to move the code to bridge rather than another generated file. Some autgenerated doc strings seem fine.

Copy link
Member

@cretz cretz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing blocking, though we may want to consider not putting user-facing components in internal bridge module. @tconley1428 - thoughts?

@VegetarianOrc VegetarianOrc force-pushed the bridge/generate_rpc_calls branch from a971dcf to 2812a80 Compare September 30, 2025 17:59
Copy link
Contributor

@tconley1428 tconley1428 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should consider

file_descriptors: list[FileDescriptor],
output_file: str = "temporalio/bridge/src/client_rpc_generated.rs",
):
print("generating bridge rpc calls")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think maybe one print for the whole thing, but I'm not sure we need all the steps. That said, marginal

futures = "0.3"
prost = "0.13"
pyo3 = { version = "0.25", features = [
"extension-module",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No good place to put this comment, but I think we should consider adding a test for the actual user issue, just to have the coverage we fixed it.

import temporalio.api.workflowservice.v1
import temporalio.bridge.client
import temporalio.bridge.proto.health.v1
import temporalio.bridge.services_generated as svc_gen
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not huge, but I don't see any good reason to move the code to bridge rather than another generated file. Some autgenerated doc strings seem fine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Bug] Batch operation feature in the Temporal Python SDK doesn't work
4 participants